package genome;


import gray.GrayTest;

import java.util.*;

import tool.Utility;

import coevolution.GGTGenerator;

public class BG extends Genotype{
	
	Random r = GGTGenerator.R;
	
	public static double MUTATION_RATE = 0.2;
	
	public BG(int sz){
		super(new double[1],sz);
		for(int i=0;i<sz;i++){
			if(r.nextBoolean()){
				genome[i] = 0;
			}
			else {
				genome[i] = 1;
			}
		}
	}
	
	public int[] toInt(int n){
		
		if(GrayTest.hashN<n){
			GrayTest.populateGreyHash(n);
		}
		
		//decode sections of n bits into integer values.
		
		if(this.size % n !=0){
			return null;
		}
	
		int[] v = new int[this.size/n];
		
		int k=0;
		for(int i=0;i<v.length;i++){
			String s = "";
			for(int j=0;j<n;j++){
				s = s+genome[k];
				k = k+1;
			}
			
			//Remove this line to turn back to a binary--> integer mapping rather than gray ---> integer
			//String s2 = GrayTest.binMap.get(s);
			String s2 = s;
			
			v[i] = Utility.binStrToInt(s2);
		}
		return v;
	}
	
	public BG flip(){
		BG flipped = (BG)this.getCopy();
		for(int i=0;i<flipped.genome.length;i++){
			if(flipped.genome[i] == 0){
				flipped.genome[i] = 1;
			}
			else {
				flipped.genome[i] = 0;
			}
		}
		return flipped;
	}

	@Override
	public double eval() {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public String getConstantString() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Genotype getCopy() {
		BG b = new BG(this.size);
		for(int i=0;i<b.size;i++){
			b.genome[i] = this.genome[i];
		}
		return b;
	}

	@Override
	public Genotype pointMutate() {
		BG g = (BG)this.getCopy();
		int i = r.nextInt(size);
		if(g.genome[i]==0){
			g.genome[i] = 1;
		}
		else {
			g.genome[i] = 0;
		}
		return g;
	}
	
	@Override
	public Genotype stoMutate(){
		BG g = (BG)this.getCopy();
		for(int i=0;i<size;i++){
			if(r.nextDouble()<MUTATION_RATE){
				if(g.genome[i]==0){
					g.genome[i] = 1;
				}
				else {
					g.genome[i] = 0;
				}
			}
		}
		return g;
	}

	public BG toBin(){
		//assumes a gray encoding and switches it binary by referencing the hashtable.
	
		//check to see if lookup table is large enough
		if(size>GrayTest.hashN){
			GrayTest.populateGreyHash(size);
		}
			
		String s = "";
		for(int i=0;i<size;i++){
			s = s+genome[i];
		}
		String sBin = GrayTest.binMap.get(""+s); //Use as key in grey map
		BG g = (BG)this.getCopy();
		
		for(int i=0;i<size;i++){
			g.genome[i]=Integer.parseInt(sBin.substring(i,i+1));
		}
		return g;
	
	}
	
	@Override
	public void setConstants(double[] d) {
		// TODO Auto-generated method stub

	}

	@Override
	public String toMathString() {
		return this.toString();
	}
	
	public BG toGray(){
		//returns the gray representation of the genotype
		
		//check to see if lookup table is large enough
		if(size>GrayTest.hashN){
			GrayTest.populateGreyHash(size);
		}
			
		String s = "";
		for(int i=0;i<size;i++){
			s = s+genome[i];
		}
		int v = Utility.binStrToInt(s); //Integer value derived from this genome
		String sGrey = GrayTest.greyMap.get(""+v); //Use as key in grey map
		BG g = (BG)this.getCopy();
		
		for(int i=0;i<size;i++){
			g.genome[i]=Integer.parseInt(sGrey.substring(i,i+1));
		}
		return g;
	}
	
	public static int[] rightShift(int[] x){
		
		int[] y = new int[x.length];
		System.arraycopy(x, 0, y, 0, x.length);
		
		for(int i=1;i<y.length;i++){
			y[i] = x[i-1];
		}
		y[0] = 0;
		return y;
	}
	
	public static void main(String args[]){
		BG g = new BG(10);
		g.printGenome();
		double[] x = g.toVector();
		System.out.println(x[0]+" "+x[1]);
		
		
	}

	@Override
	public double[] toVector() {
		
		int n = this.size; //default to 1 integer values
		int[] x= this.toInt(n);
		double[] ans = new double[x.length];
		for (int i = 0; i < ans.length; i++) {
			ans[i] = x[i];
		}
		return ans;
	}

	@Override
	public ArrayList<Genotype> generateSpace(){
		//returns whole GGT genotype space based on current parameters of this genome, using increment
		ArrayList<Genotype> space = new ArrayList<Genotype>();
		BG g = (BG)this.getCopy();
		g.clear(g.size);
		space.add(g);
		int s = 2;
		for(int i=0;i<Math.pow(s,g.size)-1;i++){
			g = g.increment();
			space.add(g);
		}
		return space;
	}
	
	@Override
	public BG increment(){
		//returns an incremented version of the current genome
		BG g = (BG)this.getCopy();
		boolean incremented = false;
		int i=0;
		while(!incremented && i<g.genome.length){
			if(genome[i]+1<=1){
				g.genome[i] = g.genome[i]+1;
				g.clear(i);
				incremented = true;
			}
			else {
				i = i+1;
			}
		}
		return g;
	}

	@Override
	public double[] getSymbolicValues() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public double distanceTo(Genotype g) {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public ArrayList<Genotype> generateSpace(int samples) {
		ArrayList<Genotype> omega = new ArrayList<Genotype>();
		
		for (int i = 0; i < samples; i++) {
			BG g = new BG(this.size);
			if(!Utility.contains(omega, g)){
				omega.add(g);
			}
			else {
				i = i-1;
			}
		}
		return omega;
	}

	@Override
	public double quickSymbolicRegression(int samples, double[] range) {
		// TODO Auto-generated method stub
		return 0;
	}

}
